/**
* ...
* @author Copyright 2007, Will Schenk, wschenk@gmail.com http://sublimeguile.com
* @licence  http://creativecommons.org/licenses/by-nc-sa/2.0/
* @Pv3D_version Exey Panteleev
* 
*/
package {
import org.papervision3d.core.geom.Lines3D;
import org.papervision3d.core.geom.renderables.Line3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.materials.special.LineMaterial;
	public class GrowingArm {
		
		public var max_len:int;
		public var growing:Boolean = false;
		public var budded:Boolean = false;
	
		public var len:Number;
		public var ideal_angle:Number;
		public var hindge_angle:Number;
		public var velocity:Number = 0;

		public var wobble_interval:Number = Math.PI/70;
		public var wobble_angle:Number = 0;
		public var wobbleValue:int = 0;

		public var spring_tension:Number = 0.25;

		public var _mass:Number = -10;

		public var knots:Array = new Array();
		public var lineContainer:Lines3D;
		public var lineMaterial:LineMaterial;

		public var line:Line3D;
		
		public var lineVertex1:Vertex3D;
		public var lineVertex2:Vertex3D;
		public var size:Number;
		
		public var oldXangle:Number;
		public var oldZangle:Number;
		
		private var globalPointLightRotation:Number = 0;
		private var globalPointLightRotationRadian:Number;
		
	public function GrowingArm( len:int, hindge_angle:Number, parent:GrowingArm, containerForDrawing:Lines3D, lineMat:LineMaterial ) {
		this.max_len = len;
		this.len = 1;
		this.ideal_angle = hindge_angle;
		this.hindge_angle = 0;	
		this.wobbleValue = int(randRange( 0, 200 ));
		this.lineContainer = containerForDrawing;
		this.lineMaterial = lineMat;
		init();
	}
	public function init():void {
		lineVertex1 = new Vertex3D(0, 0, 0);
		lineVertex2 = new Vertex3D(0, 0, 0);
		size = Math.max( .5, Math.log(mass() / 15));
		line = new Line3D(lineContainer, lineMaterial, size, lineVertex1, lineVertex2);
		lineContainer.addLine(line);
	}
	
	public function add_child( len:int, angle:Number, containerForDrawing:Lines3D, lineMat:LineMaterial ):GrowingArm {
		var child:GrowingArm = new GrowingArm( len, angle, this, containerForDrawing, lineMat );
		knots.push(child);
		//trace("GrowingArm:add_child, len " + len + " and angle " + angle + " child " + child.x + ", " + child.y)
		return child;
	}
	
	public function draw( baseAngle:Number, x:int, y:int, z:int, knotsI:int ):void {
		//trace("GrowingArm:draw");
		if( len == 0 )
		  return;
		if( len < max_len ) {
		  growing = true;
		  len += Math.max( .05, (max_len - len)/40 );
		  
		  _mass = -1;

		  if( len > (max_len / 6) ) {
			if( !budded ) {
			  budded = true;
			  for( var i:int = 0; i < knots.length; i++ ) {
				knots[i].len+=0.1;
			  }
			}
		  }
		} else {
		  growing = false;
		}
		
		var lineAngle:Number = calculateLineAngle( baseAngle );
		line.size = Math.max( .5, Math.log(mass() / 15));
		
		var endX:int = int(len * Math.cos( lineAngle ));
		endX += x;
		lineVertex1.x = x;
		lineVertex2.x = endX;
	
		var endY:int = -int(len * Math.sin( lineAngle ));
		endY += y;
		lineVertex1.y = y;
		lineVertex2.y = endY;		

		//var endZ:int = int(len * Math.atan2(endY, endX )); // Z Hack
		var endZ:int = 0
		endZ += z;
		lineVertex1.z = z;
		lineVertex2.z = endZ;
		
		for ( var i:int = 0; i < knots.length; i++ ) {
			//trace("---", i)
		  knots[i].velocity += randRange(0, .5) * velocity;
		  knots[i].draw( lineAngle, endX, endY, endZ, i );
		}
	}

	private function calculateLineAngle( base_angle:Number ):Number {
		//trace("Arm:calculateLineAngle", "base_angle", base_angle);
		var ideal_pos:Number = degrees( ideal_angle + wobble_angle );
		var hindge_pos:Number = degrees( hindge_angle );
		
		var force:Number = spring_tension * (ideal_pos - hindge_pos);
		
		var log_mass:Number = Math.log( mass() );
		
	//    float gravity = 0.1 * sin( hindge_angle ) * log_mass;

		velocity = 0.75 * (velocity + (force/log_mass));
			
		var new_pos:Number = hindge_pos + velocity;
		
		var new_angle:Number = radians( new_pos );
		
		hindge_angle = new_angle;
		
		//trace(" base_angle", base_angle, " hindge_angle", hindge_angle, " ideal_pos", ideal_pos, " hindge_pos", hindge_pos, " force", force, " diff", degrees( new_angle - hindge_angle ), " new angle", new_angle, " new angle deg", degrees( new_angle ) );
		//trace( "----------" );
	
		return base_angle + hindge_angle;	
	}
	
	private function mass():Number {
		if( _mass < 0 ) {
		  _mass = len;
		  
		  for( var i:int = 0; i < knots.length; i++ )
			_mass += knots[i].mass();
		}
		//trace(_mass, " - ", children.length);
		return _mass;	
	}
	
	
	public function wobble():void {
		wobbleValue++;
		wobble_angle = Math.cos( wobbleValue * wobble_interval )/10;	
	}
	
	/****
	 * functions for easy port from processing
	 ****/
	private function radians(degrees:Number):Number {
		return degrees * Math.PI / 180;
	}
	private function degrees(radians:Number):Number {
		return radians * 180 / Math.PI;
	}
	private function randRange(min:Number, max:Number):Number {
		var randomNum:Number = (Math.random() * (max - min + 1)) + min;
		return randomNum;
	}//randRange without floor
	
}
}